package top.whysu.gps.service.system.impl;

import cn.hutool.core.lang.Validator;
import cn.hutool.crypto.digest.DigestUtil;
import com.alibaba.fastjson.JSON;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.SecurityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import top.whysu.gps.constant.CodeConstant;
import top.whysu.gps.constant.Constant;
import top.whysu.gps.dao.relative.RelativeAccountRoleDao;
import top.whysu.gps.dao.system.AccountDao;
import top.whysu.gps.dao.system.RoleDao;
import top.whysu.gps.dto.page.PagenationDataDTO;
import top.whysu.gps.dto.system.AccountDTO;
import top.whysu.gps.dto.system.AccountQueryDTO;
import top.whysu.gps.dto.system.ModifyPwdDTO;
import top.whysu.gps.exception.BusinessException;
import top.whysu.gps.po.relative.RelativeAccountRolePO;
import top.whysu.gps.po.system.AccountPO;
import top.whysu.gps.po.system.RolePO;
import top.whysu.gps.service.system.AccountService;
import top.whysu.gps.service.system.RoleService;
import top.whysu.gps.utils.ConverterUtils;
import top.whysu.gps.utils.PageUtils;
import top.whysu.gps.utils.PwdTool;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.*;

/**
 * 系统用户业务实现类
 */
@Service("accountServiceImpl")
public class AccountServiceImpl implements AccountService {
    private static final Logger LOGGER = LoggerFactory.getLogger(AccountServiceImpl.class);
    @Resource
    private AccountDao accountDao;
    @Resource
    private RoleDao roleDao;
    @Resource
    private RoleService roleService;
    @Resource
    private RelativeAccountRoleDao relativeAccountRoleDao;

    /**
     * 新增账号
     *
     * @param accountDTO 账号传输对象
     */
    @Override
    public BigDecimal insertAccount(AccountDTO accountDTO) {
        // 基础校验
        if (Validator.isEmpty(accountDTO.getRealname())) {
            throw new BusinessException(CodeConstant.Http.PERSON_NAME_IS_NOT_NULL, "昵称不允许为空!");
        }
        if (Validator.isEmpty(accountDTO.getUsername())) {
            throw new BusinessException(CodeConstant.Http.MOBILE_NOT_NULL, "账号不允许空值!");
        }
        if (!Validator.isMobile(accountDTO.getUsername()) && !Validator.isEmail(accountDTO.getUsername())) {
            throw new BusinessException(CodeConstant.Http.MOBILE_INVALID, "账号必须是手机号或者邮箱!");
        }
        if (Validator.isEmpty(accountDTO.getRoleSign())) {
            throw new BusinessException(CodeConstant.Http.ROLE_NAME_NOT_NULL, "角色不允许空值!");
        }
        // 重复提交校验
        Map<String, Object> map = new HashMap<>();
        map.put("username", accountDTO.getUsername());
        AccountPO isinModel = accountDao.selectOne(map);
        if (isinModel != null && StringUtils.isNotBlank(isinModel.getUsername())) {
            throw new BusinessException(CodeConstant.Http.MOBILE_EXIST, "该账号已被注册!");
        }
        // 类型转换
        AccountPO accountPO = ConverterUtils.dto2po(accountDTO, AccountPO.class);
        try {
            if (StringUtils.isNotBlank(accountDTO.getPassword())) {
                accountPO.setPassword(DigestUtil.sha256Hex(accountDTO.getPassword()));
            } else {
                accountPO.setPassword(DigestUtil.sha256Hex(Constant.PASSWORD.DEFAULT));
            }
        } catch (Exception e) {
            LOGGER.error("加密密码时出现异常", e);
            throw new BusinessException(CodeConstant.Http.EXECUTE_FAIL, "加密密码时出现异常！");
        }
        // 获取当前登录用户
        String currentAccount = (String) SecurityUtils.getSubject().getPrincipal();
        accountPO.setFounder(currentAccount);
        accountDao.insert(accountPO);

        // 获取角色ID
        RolePO rolePO = roleService.selectByRoleSign(accountDTO.getRoleSign());

        // 生成账号-角色关联表
        RelativeAccountRolePO relativeAccountRolePO = new RelativeAccountRolePO();
        relativeAccountRolePO.setAccountId(accountPO.getId());
        relativeAccountRolePO.setRoleId(rolePO.getId());
        relativeAccountRoleDao.insert(relativeAccountRolePO);

        // 返回账号id
        return accountPO.getId();
    }

    /**
     * 查看账号
     *
     * @param id 主键
     * @return
     */
    @Override
    public AccountDTO selectAccount(BigDecimal id) {
        AccountPO accountPO = accountDao.selectById(id);
        return ConverterUtils.po2dto(accountPO, AccountDTO.class);
    }

    /**
     * 修改账号
     *
     * @param accountDTO 账号传输对象
     */
    @Override
    public void updateAccount(AccountDTO accountDTO) {
        if (accountDTO == null || accountDTO.getId() == null) {
            throw new BusinessException(CodeConstant.Http.INFO_ERROR, "ID不允许为空!");
        }
        if (Validator.isEmpty(accountDTO.getRealname())) {
            throw new BusinessException(CodeConstant.Http.PERSON_NAME_IS_NOT_NULL, "昵称不允许为空!");
        }
        if (Validator.isEmpty(accountDTO.getUsername())) {
            throw new BusinessException(CodeConstant.Http.MOBILE_NOT_NULL, "手机号不允许空值!");
        }
        if (!Validator.isMobile(accountDTO.getUsername())
                && !Validator.isEmail(accountDTO.getUsername())
                && !accountDTO.getUsername().equals(Constant.DefaultAccount.SUPER_ADMIN)
                && !accountDTO.getUsername().equals(Constant.DefaultAccount.ADMIN)) {
            throw new BusinessException(CodeConstant.Http.MOBILE_INVALID, "账号必须是手机号或者邮箱!");
        }
        if (Validator.isEmpty(accountDTO.getRoleSign())) {
            throw new BusinessException(CodeConstant.Http.ROLE_NAME_NOT_NULL, "角色不允许空值!");
        }
        // 根据id查询旧账号的信息
        AccountPO oldAccountPO = accountDao.selectById(accountDTO.getId());
        if (oldAccountPO == null) {
            throw new BusinessException(CodeConstant.Http.INFO_ERROR, "该账号不存在，请刷新界面重试!");
        }
        // 重复提交校验
        AccountPO validAccountPO = this.selectByUsername(accountDTO.getUsername());
        if (null != validAccountPO && !validAccountPO.getId().equals(accountDTO.getId())) {
            throw new BusinessException(CodeConstant.Http.EXECUTE_FAIL, "该账号已被注册！");
        }

        // 踢出原先的用户(需要放在update数据库之前调用)
        LOGGER.info("修改后的账号信息:{},{}, 角色:{}",
                new Object[]{accountDTO.getRealname(), accountDTO.getUsername(), accountDTO.getRoleSign()});

        AccountPO accountPO = ConverterUtils.dto2po(accountDTO, AccountPO.class);
        accountDao.update(accountPO);

        /**
         * [角色有变动时]更新账号-角色关联表
         * 1.这边的角色标识是在编辑账号时，带进来的，变更账号角色。
         * 2.如果是登录更新账号信息，则这里验证更新不会执行。
         */
        RolePO rolePO = null;
        if (null != accountDTO.getId() && null != accountDTO.getRoleSign()) {
            rolePO = roleService.selectByRoleSign(accountDTO.getRoleSign());
            RelativeAccountRolePO relativeAccountRolePO = new RelativeAccountRolePO();
            relativeAccountRolePO.setAccountId(accountDTO.getId());
            relativeAccountRolePO.setRoleId(rolePO.getId());
            relativeAccountRoleDao.updateByAccountId(relativeAccountRolePO);
        }
    }

    @Override
    public void updateLoginTime(AccountDTO accountDTO) {
        AccountPO accountPO = ConverterUtils.dto2po(accountDTO, AccountPO.class);
        accountDao.update(accountPO);
    }

    @Override
    public void updateAccountLastLoginTime(AccountPO accountPO) {
        accountPO.setLastLoginTime(new Date());
        accountDao.update(accountPO);
    }

    @Override
    public PagenationDataDTO<AccountDTO> selectPageAccount(AccountQueryDTO accountQueryDTO) {
        // 获取集合
        LinkedHashMap accountQueryMap = JSON.parseObject(JSON.toJSONString(accountQueryDTO), LinkedHashMap.class);
        // limit的起始值
        accountQueryMap.put("rowIndex", PageUtils.getRowIndex(accountQueryDTO.getPageNum(), accountQueryDTO.getPageSize()));
        List<AccountDTO> accountList = accountDao.selectViewList(accountQueryMap);
        // 获取总数
        long totalSize = accountDao.countViewList(accountQueryMap);
        if (CollectionUtils.isNotEmpty(accountList) && totalSize > 0) {
            return new PagenationDataDTO<AccountDTO>(totalSize, accountQueryDTO.getPageNum(), accountQueryDTO.getPageSize(), accountList);
        } else {
            return PageUtils.toPagedResult(null);
        }
    }

    /**
     * 删除账号
     *
     * @param id 主键
     */
    @Override
    public void deleteAccount(BigDecimal id) {
        // 账号原信息(得在删除操作之前执行)
        AccountPO oldAccount = accountDao.selectById(id);
        if (oldAccount == null) {
            throw new BusinessException(CodeConstant.Http.INFO_ERROR, "要删除的账号不存在！");
        }
        // 不能删除admin和super_admin账号
        String userName = oldAccount.getUsername();
        if (Constant.DefaultAccount.ADMIN.equals(userName) || Constant.DefaultAccount.SUPER_ADMIN.equals(userName)) {
            throw new BusinessException(CodeConstant.Http.INFO_ERROR, userName + "账号不能删除！");
        }

        AccountDTO currentAccountDTO = selectCurrentAccount();
        if (currentAccountDTO != null) {
            if (oldAccount.getId().equals(currentAccountDTO.getId())) {
                throw new BusinessException(CodeConstant.Http.INFO_ERROR, "不能删除当前登录账号！");
            }
            // 删除账号表
            accountDao.delete(id);
            // 删除账号-角色关联表
            relativeAccountRoleDao.deleteByAccountId(id);
        } else {
            throw new BusinessException(CodeConstant.Http.INFO_ERROR, "查询不到当前登录用户，请先登录！");
        }
    }

    /**
     * 用户验证
     *
     * @param account
     * @return
     */
    @Override
    public AccountPO authentication(AccountPO account) {
        Map<String, Object> params = new HashMap<>(10);
        params.put("username", account.getUsername());
        params.put("password", account.getPassword());
        return accountDao.selectOne(params);
    }

    /**
     * 根据账号查询用户
     *
     * @param username 账号
     * @return
     */
    @Override
    public AccountPO selectByUsername(String username) {
        return accountDao.selectByUsername(username);
    }

    /**
     * 重置账号密码
     *
     * @param id 主键
     */
    @Override
    public void wholeResetAccount(BigDecimal id) {
        AccountPO accountPO = accountDao.selectById(id);
        try {
            String encryptPwd = DigestUtil.sha256Hex(Constant.PASSWORD.DEFAULT);
            accountPO.setPassword(encryptPwd);
        } catch (Exception e) {
            throw new BusinessException(CodeConstant.Http.EXECUTE_FAIL, "加密密码时出现异常！");
        }
        accountDao.update(accountPO);

    }

    /**
     * 查看账号
     *
     * @return
     */
    @Override
    public AccountDTO selectCurrentAccount() {
        // 获取当前登录用户
        String currentAccount = (String) SecurityUtils.getSubject().getPrincipal();
        AccountPO accountPO = selectByUsername(currentAccount);
        AccountDTO accountDTO = ConverterUtils.po2dto(accountPO, AccountDTO.class);
        // 获取角色名称
        List<RelativeAccountRolePO> relativeAccountRolePOs = relativeAccountRoleDao.selectByAccountId(accountDTO.getId());
        for (RelativeAccountRolePO relativeAccountRolePO : relativeAccountRolePOs) {
            RolePO rolePO = roleDao.selectById(relativeAccountRolePO.getRoleId());
            if (null != rolePO) {
                accountDTO.setRoleSign(rolePO.getRoleName());
            }
        }
        return accountDTO;
    }

    /**
     * 修改密码
     *
     * @param modifyPwdDTO
     */
    @Override
    public void wholeModifyPwd(ModifyPwdDTO modifyPwdDTO) {
        // 获取当前登录用户
        String currentAccount = (String) SecurityUtils.getSubject().getPrincipal();
        AccountPO accountPO = selectByUsername(currentAccount);

        // 校验密码
        if (modifyPwdDTO.getOldPwd().isEmpty() || modifyPwdDTO.getNewPwd().isEmpty() || modifyPwdDTO.getConfirmPwd().isEmpty()) {
            throw new BusinessException(CodeConstant.Http.EXECUTE_FAIL, "数据不能为空！");
        } else {
            if (!accountPO.getPassword().equals(DigestUtil.sha256Hex(modifyPwdDTO.getOldPwd()))) {
                throw new BusinessException(CodeConstant.Http.EXECUTE_FAIL, "原密码错误！");
            } else if (modifyPwdDTO.getNewPwd().equals(modifyPwdDTO.getOldPwd())) {
                throw new BusinessException(CodeConstant.Http.EXECUTE_FAIL, "新密码和原密码不能相同！");
            } else if (!modifyPwdDTO.getConfirmPwd().equals(modifyPwdDTO.getNewPwd())) {
                throw new BusinessException(CodeConstant.Http.EXECUTE_FAIL, "确认密码与新密码不一致！");
            } else if (!PwdTool.validatePwd(modifyPwdDTO.getConfirmPwd())) {
                throw new BusinessException(CodeConstant.Http.EXECUTE_FAIL, "密码只能使用字母加数字，不允许特殊字符！");
            }
        }
        // 更新为新密码
        try {
            accountPO.setPassword(DigestUtil.sha256Hex(modifyPwdDTO.getNewPwd()));
            accountDao.update(accountPO);
        } catch (Exception e) {
            throw new BusinessException(CodeConstant.Http.EXECUTE_FAIL, "加密密码时出现异常！");
        }

    }

    @Override
    public String getCurrentLoginUser() {
        try {
            return (String) SecurityUtils.getSubject().getPrincipal();
        } catch (Exception e) {
            LOGGER.error("获取当前用户异常", e);
        }
        return null;
    }

}
